package com.readystatesoftware.viewbadger;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer.LayoutConfig;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.Image;
import ohos.agp.components.PositionLayout;
import ohos.agp.components.StackLayout;
import ohos.agp.components.TabList;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.app.Context;

/**
 * BadgeView Controller class.
 */
public class BadgeView extends Text {
    /**
     * POSITION_TOP_LEFT
     */
    public static final int POSITION_TOP_LEFT = 1;

    /**
     * POSITION_TOP_RIGHT
     */
    public static final int POSITION_TOP_RIGHT = 2;

    /**
     * POSITION_BOTTOM_LEFT
     */
    public static final int POSITION_BOTTOM_LEFT = 3;

    /**
     * POSITION_BOTTOM_RIGHT
     */
    public static final int POSITION_BOTTOM_RIGHT = 4;

    /**
     * POSITION_CENTER
     */
    public static final int POSITION_CENTER = 5;

    /**
     * DEFAULT_MARGIN_DIP
     */
    private static final int DEFAULT_MARGIN_DIP = 5;

    /**
     * DEFAULT_MARGIN_DIP
     */
    private static final int DEFAULT_LR_PADDING_DIP = 5;

    /**
     * DEFAULT_POSITION
     */
    private static final int DEFAULT_POSITION = POSITION_TOP_RIGHT;

    /**
     * DEFAULT_BADGE_COLOR
     */
    private static final int DEFAULT_BADGE_COLOR = 0xCCFF0000;

    /**
     * DEFAULT_TEXT_COLOR
     */
    private static final Color DEFAULT_TEXT_COLOR = Color.WHITE;

    /**
     * DEFAULT_TEXT_VIEW_STYLE
     */
    private static final String DEFAULT_TEXT_VIEW_STYLE = "BOLD";

    private static AnimatorProperty fadeIn;

    private static AnimatorProperty fadeOut;

    private Context context;

    private Component target;

    private int badgePosition;

    private int badgeMarginH;

    private int badgeMarginV;

    private int badgeColor;

    private boolean isShown;

    private ShapeElement badgeBg;

    private int targetTabIndex;

    /**
     * BadgeView method name
     *
     * @param context Context
     */
    public BadgeView(Context context) {
        this(context, (AttrSet) null, DEFAULT_TEXT_VIEW_STYLE);
    }

    /**
     * BadgeView method name
     *
     * @param context Context
     * @param attrs Attrset
     */
    public BadgeView(Context context, AttrSet attrs) {
        this(context, attrs, DEFAULT_TEXT_VIEW_STYLE);
    }

    /**
     * Constructor -
     *
     * @param context context for this view.
     * @param target the View to attach the badge to.
     */
    public BadgeView(Context context, Component target) {
        this(context, null, DEFAULT_TEXT_VIEW_STYLE, target, 0);
    }

    /**
     * Constructor -
     * <p>
     *
     * @param context context for this view.
     * @param target the TabWidget to attach the badge to.
     * @param index the position of the tab within the target.
     */
    public BadgeView(Context context, TabList target, int index) {
        this(context, null, DEFAULT_TEXT_VIEW_STYLE, target, index);
    }

    /**
     * BadgeView method name
     *
     * @param context Context
     * @param attrs Attrset
     * @param defStyle String
     */
    public BadgeView(Context context, AttrSet attrs, String defStyle) {
        this(context, attrs, defStyle, null, 0);
    }

    /**
     * BadgeView method name
     *
     * @param context Context
     * @param attrs Attrset
     * @param defStyle String
     * @param target Component
     * @param tabIndex integer
     */
    public BadgeView(Context context, AttrSet attrs, String defStyle, Component target, int tabIndex) {
        super(context, attrs, defStyle);
        init(context, target, tabIndex);
    }

    /**
     * init method name
     *
     * @param icontext Context
     * @param itarget Component
     * @param tabIndex integer
     */
    private void init(Context icontext, Component itarget, int tabIndex) {
        this.context = icontext;
        this.target = itarget;
        this.targetTabIndex = tabIndex;

        // apply defaults
        badgePosition = DEFAULT_POSITION;
        badgeMarginH = DEFAULT_MARGIN_DIP;
        badgeMarginV = badgeMarginH;
        badgeColor = DEFAULT_BADGE_COLOR;

        int paddingPixels = DEFAULT_LR_PADDING_DIP;
        setPadding(paddingPixels, 0, paddingPixels, 0);
        setTextColor(DEFAULT_TEXT_COLOR);

        fadeIn = new AnimatorProperty();
        fadeIn.alpha(0);
        fadeIn.setDuration(Constants.MAGIC_200);

        fadeOut = new AnimatorProperty();
        fadeOut.alpha(1);
        fadeOut.setDuration(Constants.MAGIC_200);

        isShown = false;

        if (this.target != null) {
            applyTo(this.target);
        } else {
            show();
        }
    }

    private void applyTo(Component itarget) {
        LayoutConfig lp = itarget.getLayoutConfig();
        ComponentParent parent = itarget.getComponentParent();
        StackLayout container = new StackLayout(context);

        if (itarget instanceof TabList) {
            // set itarget to the relevant tab child container
            itarget = ((TabList) itarget).getTabAt(targetTabIndex);
            this.target = itarget;

            ((PositionLayout) itarget).addComponent(container,
                new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT));

            this.setVisibility(INVISIBLE);
            container.addComponent(this);
        } else {
            ComponentParent group = (ComponentParent) parent;
            int index = group.getChildIndex(itarget);
            group.removeComponent(itarget);
            ((PositionLayout) group).addComponent(container, index, lp);
            container.addComponent(itarget);

            this.setVisibility(VISIBLE);
            container.addComponent(this);
            group.postLayout();
        }
    }

    /**
     * Make the badge show in the UI.
     */
    public void show() {
        show(false, null);
    }

    /**
     * Toggle the badge visibility in the UI.
     *
     * @param isAnimate Animation to apply to the view when made visible.
     * @param anim Animation to apply to the view when made non-visible.
     */
    private void show(boolean isAnimate, Animator anim) {
        if (getBackgroundElement() == null) {
            if (badgeBg == null) {
                badgeBg = getDefaultBackground();
            }
            setBackground(badgeBg);
        }
        applyLayoutParams();

        if (isAnimate) {
            anim.start();
        }
        this.setVisibility(VISIBLE);
        isShown = true;
    }

    /**
     * Make the badge visible in the UI.
     *
     * @param isAnimate flag to apply the default fade-in animation.
     */
    public void show(boolean isAnimate) {
        show(isAnimate, fadeIn);
    }

    /**
     * Make the badge visible in the UI.
     *
     * @param anim Animation to apply to the view when made visible.
     */
    public void show(Animator anim) {
        show(true, anim);
    }

    /**
     * Make the badge non-visible in the UI.
     */
    public void hide() {
        hide(false, null);
    }

    /**
     * Make the badge non-visible in the UI.
     *
     * @param isAnimate flag to apply the default fade-out animation.
     */
    public void hide(boolean isAnimate) {
        hide(isAnimate, fadeOut);
    }

    /**
     * Toggle views.
     *
     * @param isAnimate Boolean flag
     * @param anim Animator instance for animation.
     */
    private void hide(boolean isAnimate, Animator anim) {
        this.setVisibility(INVISIBLE);
        if (isAnimate) {
            anim.start();
        }
        isShown = false;
    }

    /**
     * Make the badge non-visible in the UI.
     *
     * @param anim Animation to apply to the view when made non-visible.
     */
    public void hide(Animator anim) {
        hide(true, anim);
    }

    /**
     * Toggle the badge visibility in the UI.
     */
    public void toggle() {
        toggle(false, null, null);
    }

    /**
     * Toggle the badge visibility in the UI.
     *
     * @param isAnimate flag to apply the default fade-in/out animation.
     */
    public void toggle(boolean isAnimate) {
        toggle(isAnimate, fadeIn, fadeOut);
    }

    /**
     * Toggle views.
     *
     * @param isAnimate Boolean flag
     * @param animIn Animator instance for animation in.
     * @param animOut Animator instance for animation out.
     */
    private void toggle(boolean isAnimate, Animator animIn, Animator animOut) {
        if (isShown) {
            hide(isAnimate && (animOut != null), animOut);
        } else {
            show(isAnimate && (animIn != null), animIn);
        }
    }

    /**
     * Toggle the badge visibility in the UI.
     *
     * @param animIn Animation to apply to the view when made visible.
     * @param animOut Animation to apply to the view when made non-visible.
     */
    public void toggle(Animator animIn, Animator animOut) {
        toggle(true, animIn, animOut);
    }

    /**
     * Increment the numeric badge label. If the current badge label cannot be converted to
     * an integer value, its label will be set to "0".
     *
     * @param offset the increment offset.
     * @return increment
     */
    public int increment(int offset) {
        CharSequence txt = getText();
        int index;
        if (txt != null) {
            try {
                index = Integer.parseInt(txt.toString());
            } catch (NumberFormatException e) {
                index = 0;
            }
        } else {
            index = 0;
        }
        index = index + offset;
        setText(String.valueOf(index));
        return index;
    }

    /**
     * Decrement the numeric badge label. If the current badge label cannot be converted to
     * an integer value, its label will be set to "0".
     *
     * @param offset the decrement offset.
     * @return increment
     */
    public int decrement(int offset) {
        return increment(-offset);
    }

    /**
     * Is this badge ShapeElement
     *
     * @return element
     */
    private ShapeElement getDefaultBackground() {
        ShapeElement element = new ShapeElement();
        element.setShape(ShapeElement.RECTANGLE);
        element.setRgbColor(new RgbColor(badgeColor));
        return element;
    }

    /**
     * Is this badge applyLayoutParams currently visible in the UI?
     */
    private void applyLayoutParams() {
        StackLayout.LayoutConfig lp;
        switch (badgePosition) {
            case POSITION_TOP_LEFT:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT,
                    Image.GRAVITY_LEFT | Image.GRAVITY_TOP);
                lp.setMargins(badgeMarginH, badgeMarginV, 0, 0);
                break;
            case POSITION_TOP_RIGHT:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT,
                    Image.GRAVITY_RIGHT | Image.GRAVITY_TOP);
                lp.setMargins(0, badgeMarginV, badgeMarginH, 0);
                break;
            case POSITION_BOTTOM_LEFT:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT,
                    Image.GRAVITY_LEFT | Image.GRAVITY_BOTTOM);
                lp.setMargins(badgeMarginH, 0, 0, badgeMarginV);
                break;
            case POSITION_BOTTOM_RIGHT:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT,
                    Image.GRAVITY_RIGHT | Image.GRAVITY_BOTTOM);
                lp.setMargins(0, 0, badgeMarginH, badgeMarginV);
                break;
            case POSITION_CENTER:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT,
                    Image.GRAVITY_CENTER);
                lp.setMargins(0, 0, 0, 0);
                break;
            default:
                lp = new StackLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
                break;
        }
        setLayoutConfig(lp);
    }

    /**
     * Returns the target View this badge has been attached to.
     *
     * @return target
     */
    public Component getTarget() {
        return target;
    }

    /**
     * Is this badge currently visible in the UI?
     *
     * @return isShow.
     */
    public boolean isShown() {
        return isShown;
    }

    /**
     * Returns the positioning of this badge.
     * <p>
     * one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT, POSITION_BOTTOM_LEFT, POSITION_BOTTOM_RIGHT, POSTION_CENTER.
     *
     * @return badgePosition
     */
    public int getBadgePosition() {
        return badgePosition;
    }

    /**
     * Set the positioning of this badge.
     *
     * @param layoutPosition one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT,
     * POSITION_BOTTOM_LEFT, POSITION_BOTTOM_RIGHT,POSTION_CENTER.
     */
    public void setBadgePosition(int layoutPosition) {
        this.badgePosition = layoutPosition;
    }

    /**
     * Returns the horizontal margin from the target View that is applied to this badge.
     *
     * @return badgeMarginH
     */
    public int getHorizontalBadgeMargin() {
        return badgeMarginH;
    }

    /**
     * Returns the vertical margin from the target View that is applied to this badge.
     *
     * @return badgeMarginV
     */
    public int getVerticalBadgeMargin() {
        return badgeMarginV;
    }

    /**
     * Set the horizontal/vertical margin from the target View that is applied to this badge.
     *
     * @param badgeMargin the margin in pixels.
     */
    public void setBadgeMargin(int badgeMargin) {
        this.badgeMarginH = badgeMargin;
        this.badgeMarginV = badgeMargin;
    }

    /**
     * Set the horizontal/vertical margin from the target View that is applied to this badge.
     *
     * @param horizontal margin in pixels.
     * @param vertical margin in pixels.
     */
    public void setBadgeMargin(int horizontal, int vertical) {
        this.badgeMarginH = horizontal;
        this.badgeMarginV = vertical;
    }

    /**
     * Returns the color value of the badge background.
     *
     * @return badgeColor
     */
    public int getBadgeBackgroundColor() {
        return badgeColor;
    }

    /**
     * Set the color value of the badge background.
     *
     * @param ibadgeColor badge color value
     */
    public void setBadgeBackgroundColor(int ibadgeColor) {
        this.badgeColor = ibadgeColor;
        badgeBg = getDefaultBackground();
    }
}